#include "Genotype.h"
#include <iostream>
Genotype::Genotype(Spectrum * sp, bool t) {
	spectrum = sp;
	for(int i=0;i < this->getSpectrum()->getOligo(0).GetLength(); i++)
	{
		this->getChain()->push_back(std::make_pair<int, NUCLEOTIDE>(1,this->getSpectrum()->getOligo(0)[i]));
	}
	mask.resize(sp->getNumberOfOligos(), 0);
	mask[0] = 1;

	GenerateGenotype();

}

Genotype::Genotype(const Genotype & gen)
{
	spectrum =gen.spectrum;
	for (int i = 0; i < gen.nucleotideChain.size(); i++)
	{
		this->getChain()->push_back(gen.nucleotideChain[i]);
	}
	mask = gen.mask;

}
bool Genotype::canInsert(OLIGO ol)
{
	if(mask[ol.number])
		return false;
	return true;
}

void Genotype::GenerateGenotype() {
	int los = -1;

	for (int i = 0; i<this->getSpectrum()->getLength()*10; i++) {
		los = rand() % this->getSpectrum()->getNumberOfOligos();
		this->AddOligoOnFirstPosition(this->getSpectrum()->getOligo(los));
	}	
	//printf("SIze: %d\n", this->Evaluate());
}

int Genotype::AddOligoOnFirstPosition(OLIGO o) {
	if( o.number!=-1 &&!canInsert(o))
		return -1;
	int writeStart=-1;
	//Calcuclate where we should start writing 
	for (int position = 0; position < this->getChain()->size() && writeStart==-1; position++)
	{
		if ((this->getChain()->at(position).second == o.val[0] 
		&& this->getChain()->at(position).first < this->getSpectrum()->getMaxOligoLength()) 
			|| this->getChain()->at(position).first == NONE) {

				int tempPosition=position+1;
				writeStart=position;

				for(int oligoPosition=1; oligoPosition < o.GetLength(); oligoPosition++,tempPosition++)
				{
					if(tempPosition < this->getSpectrum()->getLength())
					{
						if(tempPosition < this->getChain()->size())
						{
							if((this->getChain()->at(tempPosition).second != o.val[oligoPosition]
							&& this->getChain()->at(tempPosition).second!=NONE)
								|| this->getChain()->at(tempPosition).first >= this->getSpectrum()->getMaxOligoLength())
							{ //If can't fit
								writeStart=-1;
							}
						}	
					}
					else //If overflow...
					{
						writeStart=-1;
						break;
					}
				}
		}
	}


	if(writeStart == -1 
		&& this->getChain()->size() + o.GetLength() < this->getSpectrum()->getNumberOfOligos())
		writeStart=this->getChain()->size(); //if can append to the chain

	if(writeStart!=-1) //If can write
	{
		if(o.number!=-1)
		{
			mask[o.number]++;
			lastOligo = o;
		}
		for(int i=0;i < o.GetLength();i++)
		{
			if(i+writeStart < this->getChain()->size())
			{
				if(this->getChain()->at(i + writeStart).second==NONE)
					this->getChain()->at(i + writeStart).second=o[i];
				this->getChain()->at(i+writeStart).first++;

			}
			else
				this->getChain()->push_back(std::make_pair<int,NUCLEOTIDE>(1,o[i]));

		}

		//if(sp->set[o.number].second==ONE)
		//mask[o.number]=false;
	}

	return writeStart;
}



int Genotype::RemoveOligoOnFirstPosition(OLIGO o)
{
	int remStart=-1;
	for(std::vector<std::pair<int, NUCLEOTIDE> >::size_type i=1;
		i < this->getChain()->size() - o.GetLength() +1 && remStart==-1;i++)
	{

		int position=i;
		remStart=i;

		for(int j=0; j<o.GetLength() && position<this->getChain()->size() ;j++,position++)
		{
			if( !(o[j]==this->getChain()->at(position).second
				&& this->getChain()->at(position).first > 0))	
			{
				remStart=-1;
				break;
			}						
		}		
	}

	if(remStart>-1)
	{
		mask[o.number]--;
		for(int i=0;i<o.GetLength();i++)
		{
			this->getChain()->at(remStart+i).first-=1;
			if(!this->getChain()->at(remStart+i).first)
				this->getChain()->at(remStart+1).second=NONE;
		}

		//	if(sp->set[o.number].second==ONE)
		//	mask[o.number]=true;
	}
	return remStart;
}
int Genotype::MutatePositive()
{
	//int los = -1;
	//los = rand() % this->getSpectrum()->getNumberOfOligos();
	//if (mask[los]==false&&rand()%3>0)
	//;
	//else 
	//this->AddOligoOnFirstPosition(this->getSpectrum()->getOligo(los));
	//int num = this->Evaluate();
//	if (this->getSpectrum()->getNumberOfOligos() - num  < 4 && num>0);
	int num = 0;
	for(int i =1+rand() %(this->getSpectrum()->getNumberOfOligos()-1); i< this->getSpectrum()->getNumberOfOligos();i++)
		if(this->AddOligoOnFirstPosition(this->getSpectrum()->getOligo(i))>-1)
			num++;
	return num;
}

int Genotype::MutateNegative()
{
	int los = -1;
	los = rand() % this->getSpectrum()->getNumberOfOligos();
	if(this->RemoveOligoOnFirstPosition(this->getSpectrum()->getOligo(los))==-1 && lastOligo.number!=-1 )
		(this->RemoveOligoOnFirstPosition(lastOligo));
	return 1;
}

int Genotype::Evaluate()
{
	int sum=0;
	for (std::vector<std::pair<int,NUCLEOTIDE> >::iterator it=this->getChain()->begin();it!=this->getChain()->end();it++)
	{
	//	sum += it->first;
	}
	for (int i = 0; i< mask.size(); i++)
		sum+= mask[i];
	return sum;
};

void Genotype::Print() 
{
	for (std::vector<std::pair<int,NUCLEOTIDE> >::iterator it=this->getChain()->begin();it!=this->getChain()->end();it++)
	{
		switch (it->second)
		{
		case C: std::cout<<"C";break;
		case T: std::cout<<"T";break;
		case G: std::cout<<"G";break;
		case A: std::cout<<"A";break;
		default: std::cout<<" ";
		}
	}
	std::cout<<"\n";
}



Genotype * Genotype::Cross(Genotype * parent)
{
	Genotype * genotype = new Genotype(spectrum, true);

	int bestval = 0;
	int bestnum = 0;
	for (int i=0; i< parent->getNucleotideChain()->size(); i++)
	{
		int val = 0;
		for(int j =i ;j< i+ 6, j<parent->getNucleotideChain()->size(); j++)
		{
			val += parent->getNucleotideChain()->at(j).first;
		}
		if(val>bestval)
		{
			bestnum = i;
			bestval = val;
		}

	}
	OLIGO ol;
	ol.number =-1;
	for (int i=bestnum; i< parent->getNucleotideChain()->size(); i++)
	{
		ol.val.push_back(parent->getNucleotideChain()->at(i).second);
	}

	int res = genotype->AddOligoOnFirstPosition(ol);
	return genotype;
}

